package com.example.demo.services;

import com.example.demo.mapper.MusicMapper;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class MusicService {

    private final static Logger logger = LoggerFactory.getLogger(MusicService.class);
    private static final String MUSIC_DIR = "classpath:/static/music/";
    private static final String LYRICS_DIR = "classpath:/static/Lyrics/";
    private static final String BUCKET_NAME = "songs";
    private static final String LYRICS_BUCKET_NAME = "lyrics";
    private static final Pattern FILENAME_PATTERN = Pattern.compile("-\\s(.*?)\\.");
    private static final Pattern LYRICS_PATTERN = Pattern.compile("- (.*?)\\.lrc");

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private MusicMapper musicMapper;
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioService minioService;

    //    上传音乐音频文件信息到minio
    public void uploadMusic() throws IOException {
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(applicationContext)
                .getResources(MUSIC_DIR + "*");

        Arrays.stream(resources).forEach(resource -> {
            String fileName = resource.getFilename();
            if (fileName == null) {
                logger.warn("跳过无文件名的资源");
                return;
            }

            Matcher matcher = FILENAME_PATTERN.matcher(fileName);
            if (!matcher.find()) {
                logger.warn("文件名格式不匹配: {}", fileName);
                return;
            }

            String extractedContent = matcher.group(1);
            String musicKeys = musicMapper.findMusicByName(extractedContent);

            if (!StringUtils.hasText(musicKeys)) {
                logger.warn("未找到音乐键信息: {}", extractedContent);
                return;
            }

            String contentType = getContentType(fileName);
            InputStream inputStream = null;

            try {
                inputStream = resource.getInputStream();

                Map<String, String> userMetadata = new HashMap<>();
                userMetadata.put("music-keys", musicKeys);
                userMetadata.put("original-filename", fileName);

                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(BUCKET_NAME)
                        .object(fileName)
                        .stream(inputStream, resource.contentLength(), -1)
                        .contentType(contentType)
                        .userMetadata(userMetadata)
                        .build());

                logger.info("成功上传文件: {}", fileName);
            } catch (IOException | MinioException | InvalidKeyException | NoSuchAlgorithmException e) {
                logger.error("上传音频文件失败: {}, 错误: {}", fileName, e.getMessage(), e);
                throw new RuntimeException("上传音频文件失败: " + fileName, e);
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        logger.warn("关闭输入流失败: {}", fileName, e);
                    }
                }
            }
        });
    }


    //    上传音乐歌词文件到minio
    public void uploadMusicLrc() throws IOException {
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(applicationContext)
                .getResources(LYRICS_DIR + "*");
        Arrays.stream(resources).forEach(resource -> {
            String fileName = resource.getFilename();
            if (fileName == null) {
                logger.warn("跳过无文件名的资源");
                return;
            }

            Matcher matcher = LYRICS_PATTERN.matcher(fileName);
            if (!matcher.find()) {
                logger.warn("文件名格式不匹配: {}", fileName);
                return;
            }

            String extractedContent = matcher.group(1);
            String musicKeys = musicMapper.findMusicByName(extractedContent);

            if (!StringUtils.hasText(musicKeys)) {
                logger.warn("未找到音乐键信息: {}", extractedContent);
                return;
            }

//            对于lyric文件的MIME类型
            String contentType = "text/plain;charset=UTF-8";
            InputStream inputStream = null;


            try {
                inputStream = resource.getInputStream();
                Map<String, String> userMetadata = new HashMap<>();
                userMetadata.put("music-keys", musicKeys);
                userMetadata.put("original-filename", fileName);
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(LYRICS_BUCKET_NAME).object(fileName).stream(inputStream, resource.contentLength(), -1)
                        .contentType(contentType).userMetadata(userMetadata).build());
                logger.info("成功上传文件: {}", fileName);
            } catch (IOException | MinioException | InvalidKeyException | NoSuchAlgorithmException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        logger.warn("关闭输入流失败: {}", fileName, e);
                    }
                }
            }
        });
    }

    private String getContentType(String fileName) {
        if (fileName.endsWith(".flac")) {
            return "audio/flac";
        } else if (fileName.endsWith(".ogg")) {
            return "audio/ogg";
        } else if (fileName.endsWith(".mp3")) {
            return "audio/mpeg";
        } else if (fileName.endsWith(".wav")) {
            return "audio/wav";
        }
        return "application/octet-stream";
    }


    /**
     * @param musicKey 音乐信息的唯一Key
     * @return 返回音频文件的访问链接，方便在前端使用
     */
    //    获取音频文件的访问链接
    public String getMusicUrl(String musicKey) {
        try {
            Iterator<Result<Item>> objects = listObjectNames("songs");
            while (objects.hasNext()) {
                Result<Item> object = objects.next();
                String objectName = object.get().objectName();
//                获取文件元数据
                StatObjectResponse statObjectResponse = getObjectStat("songs", objectName);
                if (statObjectResponse != null) {
                    Map<String, String> userMetadata = statObjectResponse.userMetadata();
                    if (userMetadata.containsKey("music-keys")) {
                        String musicKeys = userMetadata.get("music-keys");
                        if (musicKeys.equals(musicKey)) {
//                            文件访问链接设置为一个月      好吧，minio出于安全考虑，最多只能将有效期设置为七天
                            int expirySeconds = 7 * 24 * 60 * 60;      // 三十天、一天24小时、一小时60分钟、一分钟60妙
//                            找到匹配的数据之后返回该文件的访问链接
                            String presignedUrl = getPresignedObjectUrl("songs", objectName, expirySeconds);
                            if (presignedUrl != null && !presignedUrl.isEmpty()) {
                                return presignedUrl;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.info(e.getMessage());
            throw new RuntimeException(e);
        }
        return musicKey;
    }


    //    获取歌词文件        这里不能获取访问链接，需要获取源文件
    public String getMusicLrcFile(String musicKey) {
        try {
            Iterator<Result<Item>> objects = listObjectNames("lyrics");
            while (objects.hasNext()) {
                Result<Item> object = objects.next();
                String objectName = object.get().objectName();
//                提取文件元数据
                StatObjectResponse statObjectResponse = getObjectStat("lyrics", objectName);
                if (statObjectResponse != null) {
                    Map<String, String> userMetadata = statObjectResponse.userMetadata();
                    if (userMetadata.containsKey("music-keys")) {
                        String musicKeys = userMetadata.get("music-keys");
                        if (musicKeys.equals(musicKey)) {
//                            在minio获取对象的输入流
                            try (InputStream objectsStream = minioClient.getObject(
                                    GetObjectArgs.builder().bucket(LYRICS_BUCKET_NAME).object(objectName).build()
                            )) {
//                                获取输入流中的内容
                                StringBuilder content = new StringBuilder();
//                                注意：这里要用GBK编码，使用UTF-8会出现乱码
                                try (BufferedReader reader = new BufferedReader(new InputStreamReader(objectsStream, "GBK"))) {
                                    String line;
                                    while ((line = reader.readLine()) != null) {
                                        content.append(line).append("\n");
                                    }
                                }
//                                返回文件      注意：在这里返回的时候要进行解码，不会只会收到一堆编码的内容
                                return content.toString();
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.info(e.getMessage());
            throw new RuntimeException(e);
        }
        return musicKey;
    }

    /**
     * @param bucketName minio存储桶名称
     * @param objectName minio存储桶文件对象名称
     * @return 返回minio存储桶中文件的元数据信息
     */
    //    获取minio存储桶文件的元数据信息
    public StatObjectResponse getObjectStat(String bucketName, String objectName) {
        try {
            return minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName).object(objectName).build());
        } catch (MinioException e) {
            System.err.println("Error occurred while getting object stat: " + e);
            return null;
        } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException(e);
        }
    }

    //    获取存储桶中的文件名称列表
    public Iterator<Result<Item>> listObjectNames(String bucketName) throws MinioException {
        return minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build()
        ).iterator();
    }


    /**
     * 获取对象的预签名URL。
     *
     * @param bucketName    存储桶名称
     * @param objectName    对象名称
     * @param expirySeconds 预签名URL的有效期（秒）
     * @return 预签名URL
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, int expirySeconds) {
        try {
//            生成预签名URL，有效期为expirySeconds妙
            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder().bucket(bucketName)
                            .object(objectName).method(Method.GET).expiry(expirySeconds, TimeUnit.SECONDS).build()
            );
        } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
            logger.info(e.getMessage());
            return null;
        }
    }


}